package com.demo.order.config;

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;
import io.github.resilience4j.retry.RetryRegistry;
import io.github.resilience4j.timelimiter.TimeLimiter;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import io.github.resilience4j.timelimiter.TimeLimiterRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;
import java.util.concurrent.TimeoutException;

/**
 * Resilience4j 配置类
 * 配置熔断器、重试、超时等容错机制
 */
@Slf4j
@Configuration
public class Resilience4jConfig {

    /**
     * 配置熔断器注册表
     * @return CircuitBreakerRegistry
     */
    @Bean
    public CircuitBreakerRegistry circuitBreakerRegistry() {
        // 创建熔断器配置
        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
                // 失败率阈值，超过50%失败率时触发熔断
                .failureRateThreshold(50)
                // 最小调用次数，只有在这个次数之后才会计算失败率
                .minimumNumberOfCalls(10)
                // 熔断器半开状态下允许的调用次数
                .permittedNumberOfCallsInHalfOpenState(5)
                // 熔断器从开启状态到半开状态的等待时间
                .waitDurationInOpenState(Duration.ofSeconds(30))
                // 滑动窗口大小
                .slidingWindowSize(20)
                // 滑动窗口类型：基于调用次数
                .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
                // 自动从半开状态转换到关闭状态
                .automaticTransitionFromOpenToHalfOpenEnabled(true)
                // 记录的异常类型
                .recordExceptions(Exception.class)
                // 忽略的异常类型
                .ignoreExceptions(IllegalArgumentException.class)
                .build();

        CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(circuitBreakerConfig);
        
        // 注册事件监听器
        registry.getEventPublisher().onEntryAdded(entryAddedEvent -> {
            CircuitBreaker circuitBreaker = entryAddedEvent.getAddedEntry();
            log.info("熔断器已创建: {}", circuitBreaker.getName());
            
            // 添加状态变化监听器
            circuitBreaker.getEventPublisher().onStateTransition(event -> {
                log.info("熔断器状态变化: {} - {} -> {}", 
                        circuitBreaker.getName(), 
                        event.getStateTransition().getFromState(),
                        event.getStateTransition().getToState());
            });
            
            // 添加调用成功监听器
            circuitBreaker.getEventPublisher().onSuccess(event -> {
                log.debug("熔断器调用成功: {}, 耗时: {}ms", 
                        circuitBreaker.getName(), 
                        event.getElapsedDuration().toMillis());
            });
            
            // 添加调用失败监听器
            circuitBreaker.getEventPublisher().onError(event -> {
                log.warn("熔断器调用失败: {}, 异常: {}, 耗时: {}ms", 
                        circuitBreaker.getName(), 
                        event.getThrowable().getMessage(),
                        event.getElapsedDuration().toMillis());
            });
            
            // 添加调用被拒绝监听器
            circuitBreaker.getEventPublisher().onCallNotPermitted(event -> {
                log.warn("熔断器拒绝调用: {}", circuitBreaker.getName());
            });
        });
        
        return registry;
    }

    /**
     * 配置重试注册表
     * @return RetryRegistry
     */
    @Bean
    public RetryRegistry retryRegistry() {
        // 创建重试配置
        RetryConfig retryConfig = RetryConfig.custom()
                // 最大重试次数
                .maxAttempts(3)
                // 重试间隔
                .waitDuration(Duration.ofSeconds(1))
                // 重试的异常类型
                .retryExceptions(TimeoutException.class, RuntimeException.class)
                // 不重试的异常类型
                .ignoreExceptions(IllegalArgumentException.class)
                .build();

        RetryRegistry registry = RetryRegistry.of(retryConfig);
        
        // 注册事件监听器
        registry.getEventPublisher().onEntryAdded(entryAddedEvent -> {
            Retry retry = entryAddedEvent.getAddedEntry();
            log.info("重试器已创建: {}", retry.getName());
            
            // 添加重试监听器
            retry.getEventPublisher().onRetry(event -> {
                log.warn("重试执行: {}, 第{}次重试, 异常: {}", 
                        retry.getName(), 
                        event.getNumberOfRetryAttempts(),
                        event.getLastThrowable().getMessage());
            });
            
            // 添加重试成功监听器
            retry.getEventPublisher().onSuccess(event -> {
                log.info("重试成功: {}, 总重试次数: {}", 
                        retry.getName(), 
                        event.getNumberOfRetryAttempts());
            });
            
            // 添加重试失败监听器
            retry.getEventPublisher().onError(event -> {
                log.error("重试失败: {}, 总重试次数: {}, 最终异常: {}", 
                        retry.getName(), 
                        event.getNumberOfRetryAttempts(),
                        event.getLastThrowable().getMessage());
            });
        });
        
        return registry;
    }

    /**
     * 配置超时限制器注册表
     * @return TimeLimiterRegistry
     */
    @Bean
    public TimeLimiterRegistry timeLimiterRegistry() {
        // 创建超时配置
        TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
                // 超时时间
                .timeoutDuration(Duration.ofSeconds(10))
                // 是否取消正在运行的任务
                .cancelRunningFuture(true)
                .build();

        TimeLimiterRegistry registry = TimeLimiterRegistry.of(timeLimiterConfig);
        
        // 注册事件监听器
        registry.getEventPublisher().onEntryAdded(entryAddedEvent -> {
            TimeLimiter timeLimiter = entryAddedEvent.getAddedEntry();
            log.info("超时限制器已创建: {}", timeLimiter.getName());
            
            // 添加超时监听器
            timeLimiter.getEventPublisher().onTimeout(event -> {
                log.warn("调用超时: {}", timeLimiter.getName());
            });
            
            // 添加成功监听器
            timeLimiter.getEventPublisher().onSuccess(event -> {
                log.debug("调用成功: {}", timeLimiter.getName());
            });
            
            // 添加错误监听器
            timeLimiter.getEventPublisher().onError(event -> {
                log.warn("调用失败: {}, 异常: {}", 
                        timeLimiter.getName(), 
                        event.getThrowable().getMessage());
            });
        });
        
        return registry;
    }
}